package com.rankeiot.easy.vue;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.noear.snack.ONode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.UnixDomainSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;

/**
 * 调用Node进程进行编译
 */
//@Component
public class NodeProcess {
    @Value("${server.nodejs:node}")
    private String nodePath = "node";
    private Process process;
    private String nodeVersion = null;
    private String compilerPath = null;
    private String sockPath = null;
    private SocketChannel channel;

    public NodeProcess() {
        //获取版本
        try {
            Process p = Runtime.getRuntime().exec("node -v");
            byte[] bytes = p.getInputStream().readAllBytes();
            nodeVersion = new String(bytes);
            p.destroy();
        } catch (IOException e) {
        }
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            if (process != null)
                process.destroy();
        }));
        //解压临时文件
        try {
            File jsFile = File.createTempFile("vue_compiler", ".cjs");
            InputStream source = NodeProcess.class.getResourceAsStream("/META-INF/index.cjs.gz");
            GZIPInputStream zSource = new GZIPInputStream(source);
            try (FileOutputStream outputStream = new FileOutputStream(jsFile)) {
                StreamUtils.copy(zSource, outputStream);
            } finally {
                zSource.close();
                source.close();
            }
            compilerPath = jsFile.getAbsolutePath();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //sockPath
        try {
            File sockFile = File.createTempFile("vue_compiler", ".sock");
            sockPath = sockFile.getAbsolutePath();
            sockFile.delete();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

//    public static void main(String[] args) throws IOException {
//        NodeProcess np = new NodeProcess();
//        //np.start();
//        System.out.println(np.compile("easy-vts/demo/demo.vue",false));
//    }

    //@lombok.SneakyThrows
    private void try_start() throws IOException {
        if (process == null) {
            //判断是否有node
            if (nodeVersion == null) {
                throw new RuntimeException("未检测的nodejs,请安装nodejs并配置环境变量，或者配置server.nodejs路径");
            }
            //创建零时node执行文件
            ProcessBuilder pu = new ProcessBuilder(nodePath, compilerPath, sockPath);
            pu.directory(new File("").getAbsoluteFile());
            pu.inheritIO();
            process = pu.start();
            process.onExit().thenRun(() -> {
                process = null;
                File file = new File(sockPath);
                if (file.exists()) {
                    file.delete();
                }
            });
            File sockFile=new File(sockPath);
            while (!sockFile.exists()){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            UnixDomainSocketAddress socketAddress = UnixDomainSocketAddress.of(sockPath);
            channel = SocketChannel.open(socketAddress);
        }
    }
    public static class CompileResult{
        boolean success;
        String msg;
    }
    public boolean compile(String file, boolean debug) {
        try {
            try_start();
            Map args = new HashMap();
            args.put("file", file);
            args.put("debug", debug);
            String param = ONode.serialize(args);

            channel.write(ByteBuffer.wrap(param.getBytes(StandardCharsets.UTF_8)));
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            int readBytes = channel.read(readBuffer);
            readBuffer.flip();

            String resultStr = new String(readBuffer.array(), 0, readBytes);
            CompileResult result =ONode.deserialize(resultStr,CompileResult.class);
            return result.success;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }
}
